home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Developer / BBFig / Source / YapDocument.m < prev    next >
Text File  |  1992-05-13  |  12KB  |  442 lines

  1. /*
  2.  *  YapDocument.m
  3.  *  Author: Ali Ozer
  4.  *  Created: Aug 28, 1988
  5.  *  Modified for 0.8: Sep 1988
  6.  *  Modified for 0.9 and revised: Feb & Mar 1989
  7.  *  Modified for 1.0 and nibified: Jun & Jul 1989
  8.  *  Modified for 2.0 and zonified: Aug 1990
  9.  *  Modified: Jan 92 for BBFig by Izumi Ohzawa (izumi@pinoko.berkeley.edu)
  10.  *
  11.  *  YapDocument class implements the Yap documents --- for every open 
  12.  *  window, we have another instance of the YapDocument class. Each instance
  13.  *  loads itself into a separate zone.
  14.  *
  15.  *  You may freely copy, distribute and reuse the code in this example.
  16.  *  NeXT disclaims any warranty of any kind, expressed or implied,
  17.  *  as to its fitness for any particular use.
  18.  */
  19.  
  20. #import "PSText.h"
  21. #import "YapDocument.h"
  22. #import "YapApp.h"
  23. #import "YapOutput.h"
  24.  
  25. #import <appkit/nextstd.h>
  26. #import <appkit/Application.h>
  27. #import <appkit/Font.h>
  28. #import <appkit/PrintInfo.h>
  29. #import <appkit/SavePanel.h>
  30. #import <appkit/ScrollView.h>
  31. #import <appkit/Window.h>
  32. #import <appkit/TextField.h>
  33. #import <appkit/Panel.h>
  34. #import <appkit/Text.h>
  35. #import <objc/List.h>
  36. #import <streams/streams.h>
  37. #import <defaults.h>
  38. #import <mach.h>
  39. #import <string.h>
  40. #import <zone.h>
  41.  
  42. #define XOFFSET 5.0     // Offset of subsequent windows
  43. #define YOFFSET -20.0
  44. #define MAXSIZE 1.0e38    // Maximum size of a text object
  45.  
  46. #define UNTITLED "UNTITLED"
  47.  
  48. @implementation YapDocument
  49.  
  50. static  char strbuf[128];
  51.  
  52. /*
  53.  * The next two methods allow us to cache/reuse zones.
  54.  */
  55. static id zoneList = nil;
  56.  
  57. + (NXZone *)newZone
  58. {
  59.     if (!zoneList || ![zoneList count]) {
  60.     return NXCreateZone(vm_page_size, vm_page_size, YES);
  61.     } else {
  62.     return (NXZone *)[zoneList removeLastObject];
  63.     }
  64. }
  65.  
  66. + (void)reuseZone:(NXZone *)aZone
  67. {
  68.     if (!zoneList) zoneList = [List new];
  69.     [zoneList addObject:(id)aZone];
  70. }
  71.  
  72.  
  73. /*
  74.  * Create a new instance of YapDocument with the specified file in the
  75.  * buffer.  If the file cannot be opened, no document is created and nil
  76.  * is returned.
  77.  */
  78. + newFromFile:(const char *)fileName
  79. {
  80.     NXStream *stream = NULL;
  81.     id docWin;        /* Window belonging to this document. */
  82.     id textObj;        /* The text object we put in the window. */
  83.     NXRect textFrame;    /* The frame of the text object in our window */
  84.  
  85.     if (fileName && !(stream = NXMapFile(fileName, NX_READONLY))) {
  86.     return nil;
  87.     }
  88.  
  89.     self = [[self allocFromZone:[self newZone]] init];
  90.     
  91.     if (![NXApp loadNibSection:"Document.nib" owner:self withNames:NO fromZone:[self zone]]) {
  92.     NXCloseMemory (stream, NX_FREEBUFFER);
  93.         [self free];
  94.     return nil;
  95.     }
  96.  
  97.     /*
  98.      * Loading the nib file above sets the document outlet to the
  99.      * scrollview that contains PS code;
  100.      * so we can use this outlet to get at the window & such.
  101.      */
  102.     docWin = [document window];
  103.     [[document docView] getFrame:&textFrame];
  104.  
  105.     /*
  106.      * Put the window offset from the previous document window... If no
  107.      * previous window exists, or the main window is undetermined, then
  108.      */ 
  109.     if ([NXApp mainWindow]) {
  110.     NXRect winFrame, winLoc;
  111.     [[NXApp mainWindow] getFrame:&winFrame];
  112.     [[docWin class] getContentRect:&winLoc
  113.             forFrameRect:&winFrame
  114.             style:[docWin style]];
  115.     [docWin moveTo:NX_X(&winLoc) + XOFFSET :NX_Y(&winLoc) + YOFFSET];
  116.     }
  117.     
  118.     [self setName:UNTITLED];
  119.     [docWin setDelegate:self];
  120.  
  121.     if (stream) {
  122.     char *text;
  123.     int len, maxLen;
  124.     NXGetMemoryBuffer (stream, &text, &len, &maxLen);
  125.     textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame text:text alignment:NX_LEFTALIGNED];
  126.     [self setName:fileName];
  127.     NXCloseMemory (stream, NX_FREEBUFFER);
  128.     } else {
  129.     textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame];
  130.     [self setName:UNTITLED];
  131.     }
  132.  
  133.     /*
  134.      * Put this new text object in the window and free the IB-created one.
  135.      */
  136.     [[document setDocView:textObj] free];
  137.  
  138.     /*
  139.      * Set various parameters.
  140.      */
  141.     [document setAutoresizeSubviews:YES];
  142.     [textObj setVertResizable:YES];        // Grow down as you type
  143.     [textObj setHorizResizable:NO];        // Let it do sideways too
  144.     [textObj setMonoFont:YES];
  145.     [textObj setOpaque:YES];
  146.     [textObj setMinSize:&textFrame.size];
  147.     NX_WIDTH(&textFrame) = NX_HEIGHT(&textFrame) = MAXSIZE;
  148.     [textObj setMaxSize:&textFrame.size];    // Can grow
  149.     [textObj setSel:0:0];            // Set the selection
  150.     [textObj setDelegate:self];
  151.     [textObj sizeToFit];
  152.  
  153.     [docWin makeKeyAndOrderFront:self];
  154.  
  155.     [self initializePrintInfo];
  156.  
  157.     return self;
  158. }
  159.  
  160. + new
  161. {
  162.     return [self newFromFile:NULL];
  163. }
  164.  
  165. - initializePrintInfo
  166. {
  167.     static BOOL printInfoInitialized = NO;
  168.     if (!printInfoInitialized) {
  169.     [[NXApp printInfo] setVertCentered:NO];
  170.     [[NXApp printInfo] setHorizCentered:NO];
  171.     [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
  172.     [[NXApp printInfo] setMarginLeft:36.0 right:36.0 top:72.0 bottom:72.0];
  173.     printInfoInitialized = YES;
  174.     }
  175.     return self;
  176. }
  177.  
  178. /*
  179.  * Delegate method for the document Text object. We use this method
  180.  * to detect when the text in the window is modified.
  181.  */
  182. - text:text isEmpty:(BOOL)empty
  183. {
  184.     if (![[document window] isDocEdited]) {
  185.         [[document window] setDocEdited:YES];
  186.     }
  187.     return NO;
  188. }
  189.  
  190. /*
  191.  * Delegate method for the document Text object. We use this method
  192.  * to detect when the font is changed so we an write it out as the default.
  193.  */
  194. - textWillConvert:textObject fromFont:oldFont toFont:newFont
  195. {
  196.     if (newFont) {
  197.         char str[80];
  198.     sprintf (str, "%f\0", [newFont pointSize]);
  199.     NXWriteDefault ([NXApp appName], "NXFontSize", str);
  200.     NXWriteDefault ([NXApp appName], "NXFont", [newFont name]);
  201.     [Text setDefaultFont:newFont];
  202.     }
  203.  
  204.     return newFont;
  205. }
  206.  
  207. // This fixes the problem of messed up text when you resize the
  208. // document window.
  209. - windowDidResize:sender
  210. {
  211.     id docText;
  212.     docText = [document docView];
  213.     [docText calcLine];
  214.     [docText sizeToFit];
  215.     return self;
  216. }
  217.  
  218. /*
  219.  * saveDocument: will write out the contents of the document
  220.  * to the specified file. The best (perhaps not the cleanest but the
  221.  * most efficient) way to dump a Text object to a file seems to be
  222.  * to open the file (with open()), then to use NXOpenFile(), and
  223.  * finally use writeText: to dump the contents out. 
  224.  */
  225. - (int)saveDocument:(const char *)fileName
  226. {
  227.     BOOL saveOK;
  228.     int fd;     // File descriptor
  229.     int retval = 0;
  230.     NXStream *stream = NULL;
  231.  
  232.     if (saveOK = 
  233.       (((fd = open (fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644)) != -1) &&
  234.             (stream = NXOpenFile (fd, NX_WRITEONLY))))
  235.     [[document docView] writeText:stream];
  236.     if (stream) NXClose (stream);
  237.     if (fd != -1) close (fd);
  238.  
  239.     if (saveOK) {
  240.           [self setName:fileName];
  241.     [[document window] setDocEdited:NO];
  242.     } else {
  243.         if(NXRunAlertPanel(NULL, "Can't write file.", "OK", "Save As", NULL)
  244.         == NX_ALERTALTERNATE)
  245.         retval = -1;
  246.     }
  247.     return retval;
  248. }
  249.  
  250. /*
  251.  * Set/Get the name of the document. This is the same as the title.
  252.  */
  253. // There was a bug in the original Yap v19, where filename changes for
  254. // no reason whan saved without executed (happens when multiple files open.
  255. // The bug has been fixed.  The problem was that "name" and "documentName"
  256. // are the identical pointers to the same memory for "Save".  And the orig
  257. // code was freeing "name" before documentName was copied to a new location.
  258.  
  259. - setName:(const char *)documentName
  260. {
  261.   char *temp;
  262.  
  263.     documentName = documentName ? documentName : "";
  264.     temp = NXCopyStringBufferFromZone (documentName, [self zone]);
  265.     if (name) free(name);
  266.     name = temp;
  267.     [[document window] setTitleAsFilename:name];
  268.     return self;
  269. }
  270.  
  271. -(const char *)name
  272. {
  273.     return name;
  274. }
  275.  
  276. /*
  277.  * windowWillClose: gets called by windows who have this instance of 
  278.  * YapDocument as delegate. The document simply marks itself and its zone
  279.  * to be free and lets the window know it can close.  A more sophisticated
  280.  * program might want to put up an alert if the document was edited but not
  281.  * saved.
  282.  */
  283. - windowWillClose:sender action:(const char *)action
  284. {
  285.     if([[document window] isDocEdited])
  286.     {
  287.         int dstatus = NXRunAlertPanel(action,
  288.                 "Save edited document: %s ?",
  289.                     "Save", "Don't Save", "Cancel", name);
  290.     if(dstatus == NX_ALERTDEFAULT)        /* save */
  291.         [self save:self];
  292.     else if(dstatus == NX_ALERTOTHER)    /* cancel */
  293.         return nil;
  294.     }
  295.     [sender setDelegate:nil];
  296.     [self free];
  297.     return sender;  // We need to return a non-nil value --- here's one.
  298.  
  299. }
  300.  
  301. - windowWillClose:sender
  302. {
  303.     return [self windowWillClose:sender action:"Close"];
  304. }
  305.  
  306.  
  307. /*
  308. - textWillChange:sender
  309. {
  310.     [[document window] setDocEdited:YES];
  311.     return sender;
  312. }
  313. */
  314.  
  315. - free
  316. {
  317.     NXZone *docZone = [self zone];
  318.     if (name) free(name);
  319.     [super free];
  320.     [YapDocument reuseZone:docZone];
  321.     return nil;
  322. }
  323.     
  324. /*
  325.  * save: saves the current document. If the document is untitled, it 
  326.  * puts up a savePanel to get the user to enter a file name. saveAs:
  327.  * saves the document under a new name by putting up a savePanel.
  328.  */
  329. - save:sender 
  330. {
  331.     const char *fileName = [self name];
  332.  
  333.     if ((fileName == NULL) ||
  334.     (strcmp (fileName, UNTITLED) == 0) || (strcmp (fileName, "") == 0))
  335.     {  /* fileName is not valid, get a new one */
  336.       if ([[SavePanel new] runModalForDirectory:"." file:UNTITLED] == NO) 
  337.     return self;
  338.       else
  339.     fileName = [[SavePanel new] filename];
  340.     }
  341.  
  342.     if (fileName)
  343.        if([self saveDocument:fileName])
  344.           [self saveAs:self];    /* Couldn't save -- try another name */
  345.   
  346.     return self;
  347. }
  348.   
  349. - saveAs:sender 
  350. {
  351.   again:
  352.     if ([[SavePanel new] runModalForDirectory:"." file:[self name]])
  353.     {
  354.         if([self saveDocument:[[SavePanel new] filename]])
  355.        goto again;
  356.     }
  357.     return self;
  358. }
  359.  
  360. - execute:sender
  361. {
  362.   float llx, lly, urx, ury, bbm;
  363.   int utime;
  364.     [[NXApp outputView] executeCodeFrom:[document docView]
  365.                 andReturnBB: &llx : &lly : &urx : &ury
  366.             usertime: &utime];
  367.     bbm = (float)[NXApp bbMargin];
  368.     if(utime >= 0)
  369.     {
  370.       llx -= bbm;
  371.       lly -= bbm;
  372.       urx += bbm;
  373.       ury += bbm;
  374.       sprintf(strbuf, "%%%%BoundingBox: %.0f %.0f %.0f %.0f", llx, lly, urx, ury);
  375.     }
  376.     else
  377.       strcpy(strbuf,"");
  378.     [bbTextField setStringValue: strbuf];
  379.     return self;
  380. }   
  381.  
  382. /*
  383.  * To get around the problem of printPSCode: going up the responder chain and
  384.  * causing print panel to come back after Cancel, we use the following glue.
  385.  */
  386. - print:sender
  387. {
  388.     [[document docView] printPSCode:sender];
  389.     return self;
  390. }
  391.  
  392. /*
  393.  * Insert %%BoundigBox comment in "bbTextField" into PSText
  394.  */
  395. - insertBB:sender
  396. {
  397.     id docText;
  398. //    int selstart,selend;
  399.     docText = [document docView];
  400. //    [docText getSel: &selstart : &selend];
  401.     strncpy(strbuf, [bbTextField stringValue], 80);
  402.     strcat(strbuf, "\n");
  403.     [[document window] setDocEdited:YES];
  404.     [docText replaceSel: strbuf];
  405.  
  406.     return self;
  407. }
  408.  
  409. /*
  410.  * Insert %!PS-Adobe-2.0 EPSF-2.0 comment in "versionField" into PSText
  411.  */
  412. - insertVersion:sender
  413. {
  414.     id docText;
  415.     docText = [document docView];
  416.     [docText setSel:0 :0];
  417.     strncpy(strbuf, [versionField stringValue], 40);
  418.     strcat(strbuf,"\n");
  419.     [[document window] setDocEdited:YES];
  420.     [docText replaceSel: strbuf];
  421.     return self;
  422. }
  423.  
  424. /* Insert both of the above at the Top of the PSText */
  425. - insertBoth:sender
  426. {
  427.     id docText;
  428.     docText = [document docView];
  429.     [docText setSel:0 :0];
  430.     strncpy(strbuf, [versionField stringValue], 40);
  431.     strcat(strbuf,"\n");
  432.     strncat(strbuf, [bbTextField stringValue], 80);
  433.     strcat(strbuf, "\n");
  434.     [[document window] setDocEdited:YES];
  435.     [docText replaceSel: strbuf];
  436.     return self;
  437. }
  438.  
  439.  
  440.  
  441. @end
  442.